﻿' ----------------------------------------------------------------------------------
' Microsoft Developer & Platform Evangelism
' 
' Copyright (c) Microsoft Corporation. All rights reserved.
' 
' THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
' EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
' OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ----------------------------------------------------------------------------------
' The example companies, organizations, products, domain names,
' e-mail addresses, logos, people, places, and events depicted
' herein are fictitious.  No association with any real company,
' organization, product, domain name, email address, logo, person,
' places, or events is intended or should be inferred.
' ----------------------------------------------------------------------------------

Imports Microsoft.WindowsAzure
Imports Microsoft.Samples.WindowsPhoneCloud.Web.Serializers
Imports Microsoft.Samples.WindowsPhoneCloud.Web.Infrastructure
Imports System.Xml
Imports System.Net
Imports System.IO
Imports System.Globalization

Namespace Handlers

    Public MustInherit Class StorageProxyHandler
        Implements IHttpHandler
        Private ReadOnly formatSerializerFactory As IFormatSerializerFactory
        Private webRequestCreatorValue As IWebRequestCreate

        Protected Sub New(ByVal cloudStorageAccount As CloudStorageAccount, ByVal requestValidator As IStorageRequestValidator, ByVal formatSerializerFactory As IFormatSerializerFactory)
            Me.RequestValidator = requestValidator
            Me.formatSerializerFactory = formatSerializerFactory

            Me.CloudStorageAccount = cloudStorageAccount
        End Sub

        Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
            Get
                Return True
            End Get
        End Property

        Friend Property WebRequestCreator() As IWebRequestCreate
            Get
                If Me.webRequestCreatorValue Is Nothing Then
                    Me.webRequestCreatorValue = New WebRequestCreator()
                End If

                Return Me.webRequestCreatorValue
            End Get

            Set(ByVal value As IWebRequestCreate)
                Me.webRequestCreatorValue = value
            End Set
        End Property

        Private privateCloudStorageAccount As CloudStorageAccount
        Protected Property CloudStorageAccount() As CloudStorageAccount
            Get
                Return privateCloudStorageAccount
            End Get
            Private Set(ByVal value As CloudStorageAccount)
                privateCloudStorageAccount = value
            End Set
        End Property

        Private privateRequestValidator As IStorageRequestValidator
        Protected Property RequestValidator() As IStorageRequestValidator
            Get
                Return privateRequestValidator
            End Get
            Private Set(ByVal value As IStorageRequestValidator)
                privateRequestValidator = value
            End Set
        End Property

        Protected MustOverride ReadOnly Property AzureStorageUrl() As String

        Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
            If Me.RequestValidator.ValidateRequest(context) Then
                Dim azureResponse = Me.GetAzureStorageResponse(Me.GetAzureStorageRequestUri(context.Request), context.Request)

                Dim serializer = Me.formatSerializerFactory.GetSerializer(context.Request.Headers, context.Request.QueryString)
                Dim azureStorageResponseBody = azureResponse.ExtractBodyString()
                Dim serializedContentType = String.Empty
                If Not String.IsNullOrWhiteSpace(azureStorageResponseBody) Then
                    Dim xmlDoc = New XmlDocument()
                    xmlDoc.LoadXml(azureStorageResponseBody)

                    azureStorageResponseBody = serializer.SerializeReply(xmlDoc, serializedContentType)
                End If

                azureResponse.CopyResponseHeadersTo(context.Response, serializedContentType)

                context.Response.Write(Me.GetProxyResponseBody(azureStorageResponseBody, context.Request))

                Me.PostProcessProxyRequest(context)

                context.Response.End()
            End If
        End Sub

        Protected Overridable Function GetAzureStorageRequestBody(ByVal proxyRequestBody As String, ByVal proxyRequest As HttpRequest) As String
            Dim oldValue = String.Format(CultureInfo.InvariantCulture, "{0}{1}", proxyRequest.Url.GetComponents(UriComponents.SchemeAndServer And (Not UriComponents.Port), UriFormat.SafeUnescaped), proxyRequest.FilePath)
            Dim newValue = Me.AzureStorageUrl

            Return proxyRequestBody.Replace(oldValue, newValue)
        End Function

        Protected Overridable Function GetProxyResponseBody(ByVal azureStorageResponseBody As String, ByVal proxyRequest As HttpRequest) As String
            Dim oldValue = Me.AzureStorageUrl
            Dim newValue = String.Format(CultureInfo.InvariantCulture, "{0}{1}", proxyRequest.Url.GetComponents(UriComponents.SchemeAndServer And (Not UriComponents.Port), UriFormat.SafeUnescaped), proxyRequest.FilePath)

            Return azureStorageResponseBody.Replace(oldValue, newValue)
        End Function

        Protected MustOverride Sub SignRequest(ByVal webRequest As WebRequest)

        Protected MustOverride Sub PostProcessProxyRequest(ByVal context As HttpContext)

        Private Function GetAzureStorageRequestUri(ByVal request As HttpRequest) As Uri
            Dim relativeUrl = If(request.QueryString.Count > 0, String.Format(CultureInfo.InvariantCulture, "{0}?{1}", request.PathInfo, request.QueryString.ToString()), request.PathInfo)

            Return New Uri(String.Format(CultureInfo.InvariantCulture, "{0}{1}", Me.AzureStorageUrl, relativeUrl), UriKind.Absolute)
        End Function

        Private Function GetAzureStorageResponse(ByVal azureStorageRequestUri As Uri, ByVal proxyRequest As HttpRequest) As WebResponse
            Dim azureStorageRequest = Me.WebRequestCreator.Create(azureStorageRequestUri)
            Dim azureStorageRequestBody = Me.GetAzureStorageRequestBody(New StreamReader(proxyRequest.InputStream).ReadToEnd(), proxyRequest)

            azureStorageRequest.Method = proxyRequest.HttpMethod
            If azureStorageRequestBody.Length > 0 OrElse ((Not proxyRequest.HttpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase)) AndAlso (Not proxyRequest.HttpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase))) Then
                azureStorageRequest.ContentLength = azureStorageRequestBody.Length
            End If

            proxyRequest.CopyRequestHeadersTo(azureStorageRequest)

            Me.SignRequest(azureStorageRequest)

            If (Not proxyRequest.HttpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase)) AndAlso (Not proxyRequest.HttpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) AndAlso proxyRequest.ContentLength > 0 Then
                Dim azureStorageRequestStream = azureStorageRequest.GetRequestStream()
                Using writer = New StreamWriter(azureStorageRequestStream)
                    azureStorageRequestStream = Nothing
                    writer.Write(azureStorageRequestBody)
                End Using
            End If

            Try
                Return azureStorageRequest.GetResponse()
            Catch webException As WebException
                Return webException.Response
            End Try
        End Function
    End Class
End Namespace